import 'package:flutter/material.dart';
import 'package:tile/controller/mixin.dart';
import 'package:tile/data/board.dart';
import 'package:tile/view/painter/painter.dart';

import 'board_style.dart';

///ai分析的颜色，必须是4种，由深到浅
List<Color> colorsAiAnalysis = [
  const Color.fromRGBO(0, 242, 238, 1.0),
  const Color.fromRGBO(48, 209, 21, 1.0),
  const Color.fromRGBO(255, 236, 0, 1.0),
  const Color.fromRGBO(220, 207, 38, 0.7),
];

///ai一手的颜色，必须是4种，由深到浅
List<Color> colorsAiNext = [
  const Color.fromRGBO(255, 0, 0, 1.0),
  const Color.fromRGBO(235, 80, 80, 1.0),
  const Color.fromRGBO(240, 110, 150, 1.0),
  const Color.fromRGBO(240, 170, 200, 1.0),
];

Color bestChessColor = Color(0xff8525FC);

class AnalysisPoint {
  String position = '';
  int x = 0;
  int y = 0;
  int visits = -1;
  double winRate = 0;
  double scoreLead = 0;
  String pv = '';

  //缓存计算后的颜色索引
  int colorIndex = 0;

  String get visitsString =>
      (visits > 10 * 1000) ? "${visits ~/ 1000}k" : visits.toString();
}

class AnalysisData {
  List<AnalysisPoint> data = List.empty(growable: true);
  final List<AnalysisPoint> _analysisDataTemp = List.empty(growable: true);

  sort([int Function(AnalysisPoint a, AnalysisPoint b)? compare]) =>
      data.sort(compare);

  int get length => data.length;

  bool get isNotEmpty => data.isNotEmpty;

  bool get isEmpty => data.isEmpty;

  AnalysisPoint operator [](int index) => data[index];

  clear() => data.clear();

  addAll(List<AnalysisPoint> points) => data.addAll(points);

  hide() {
    _analysisDataTemp.clear();
    _analysisDataTemp.addAll(data);
    data.clear();
  }

  restore() {
    data.clear();
    data.addAll(_analysisDataTemp);
    _analysisDataTemp.clear();
  }
}

mixin AINextMixin on TileViewControllerMixin {
  ///AI一手信息UI
  OverlayEntry? _aiNextUI;

  OverlayEntry? _buildAiNext(
      List<AnalysisPoint> points, Function(int index) onClick) {
    RenderObject? renderObject;
    try {
      renderObject = context.findRenderObject();
    } catch (e) {
      return null;
    }
    if (renderObject == null) return null;
    RenderBox renderBox = renderObject as RenderBox;
    var parentWidth = renderBox.size.width;
    var parentHeight = renderBox.size.height;
    var height = parentHeight / 20 - 2;

    List<Widget> buildPoint() {
      List<Widget> children = [];
      for (int i = 0; i < points.length; i++) {
        children.add(SizedBox(
          width: height * 0.8,
        ));
        children.add(GestureDetector(
          onTap: () {
            onClick.call(i);
          },
          child: Container(
            width: height,
            height: height,
            decoration: BoxDecoration(
                color: colorsAiNext[i],
                borderRadius: BorderRadius.circular(height)),
            alignment: Alignment.center,
            child: Text(
              "${points[i].winRate}",
              style: TextStyle(fontSize: height / 3),
            ),
          ),
        ));
      }
      return children;
    }

    var children = buildPoint();
    children.insert(
        0,
        Text(
          "推荐点：",
          style: TextStyle(
              color: const Color.fromRGBO(246, 189, 119, 1.0),
              fontSize: height * 0.55),
        ));

    var child = Container(
      width: parentWidth,
      height: height,
      alignment: Alignment.center,
      child: DecoratedBox(
        decoration: BoxDecoration(
            color: const Color.fromRGBO(0, 0, 0, 0.7),
            borderRadius: BorderRadius.circular(5)),
        child: Padding(
          padding: EdgeInsets.symmetric(horizontal: height * 0.8),
          child: Row(
            mainAxisSize: MainAxisSize.min,
            children: children,
          ),
        ),
      ),
    );
    return OverlayEntry(builder: (context) {
      return UnconstrainedBox(
        child: CompositedTransformFollower(
          link: tileLayerLink,
          followerAnchor: Alignment.bottomCenter,
          targetAnchor: Alignment.bottomCenter,
          child: child,
        ),
      );
    });
  }

  ///展示AI一手信息
  showAINextInfo(
      {required List<AnalysisPoint> points,
      required Function(int index) onClick}) {
    hideAINextInfo();
    _aiNextUI = _buildAiNext(points, onClick);
    if (_aiNextUI != null) Overlay.of(context).insert(_aiNextUI!);
  }

  ///隐藏AI一手信息
  hideAINextInfo() {
    _aiNextUI?.remove();
    _aiNextUI = null;
  }
}

extension BoardAnalysisExt on Board {
  AnalysisData get analysisData {
    if (!currNode.cache.containsKey(tileAnalysis)) {
      currNode.cache[tileAnalysis] = AnalysisData();
    }
    return currNode.cache[tileAnalysis];
  }

  ///展示分析点数据
  showAnalysis(List<AnalysisPoint> data, {bool refreshUI = false}) {
    analysisData.clear();

    //计算每个分析点的颜色索引
    if (data.isNotEmpty) {
      data = data.toList(growable: true);
      data.sort((a, b) {
        return b.visits - a.visits;
      });
      for (int i = 0; i < (data.length); i++) {
        final element = data[i];
        Color color = i == 0
            ? colorsAiAnalysis[0]
            : i <= 5
                ? colorsAiAnalysis[1]
                : colorsAiAnalysis[2];

        // 颜色二次加工
        if (element.visits < 5) {
          color = colorsAiAnalysis[3];
        } else if (element.visits > 100 && i > 1) {
          color = colorsAiAnalysis[1];
        }

        element.colorIndex = colorsAiAnalysis.indexOf(color);
      }
    }

    analysisData.addAll(data);

    if (refreshUI) postEvent('showAnalysis');
  }

  ///清除分析点数据
  clearAnalysis({bool refreshUI = false}) {
    if (analysisData.isEmpty == true) return;
    analysisData.clear();
    if (refreshUI) postEvent('clearAnalysis');
  }

  AnalysisData get analysisDataAINext {
    if (!currNode.cache.containsKey(tileAnalysisAINext)) {
      currNode.cache[tileAnalysisAINext] = AnalysisData();
    }
    return currNode.cache[tileAnalysisAINext];
  }

  ///展示分析点数据
  showAnalysisAINext(List<AnalysisPoint> data, {bool refreshUI = false}) {
    analysisDataAINext.clear();

    //计算每个分析点的颜色索引
    if (data.isNotEmpty) {
      data = data.toList(growable: true);
      data.sort((a, b) {
        if (b.winRate > a.winRate) {
          return (b.winRate - a.winRate).ceil();
        } else {
          return (b.winRate - a.winRate).floor();
        }
      });

      for (int i = 0; i < (data.length); i++) {
        final element = data[i];
        element.colorIndex = i;
      }
    }

    analysisDataAINext.addAll(data);

    if (refreshUI) postEvent('showAnalysis');
  }

  ///清除分析点数据
  clearAnalysisAINext({bool refreshUI = false}) {
    if (analysisDataAINext.isEmpty == true) return;
    analysisDataAINext.clear();
    if (refreshUI) postEvent('clearAnalysis');
  }
}

class AnalysisDataPainter extends BoardPainter {
  final _paint = Paint();

  @override
  paint(
    Canvas canvas, {
    Board? board,
    required int boardSize,
    required double Function(int num) toScreen,
    required double tileSize,
    required bool isThumb,
    required Rect rect,
    dynamic nodeExtraData,
  }) {
    final AnalysisData analysisData = nodeExtraData;

    if (analysisData.isEmpty) return;
    if (analysisData[0].visits >= 0) {
      paintAnalysis(analysisData, board, boardSize, toScreen, canvas, tileSize);
    } else {
      paintAINext(analysisData, toScreen, canvas, tileSize);
    }
  }

  void paintAnalysis(AnalysisData analysisData, Board? board, int boardSize,
      double Function(int num) toScreen, Canvas canvas, double tileSize) {
    for (int i = 0; i < (analysisData.length); i++) {
      var element = analysisData[i];
      if (element.x < 1 ||
          element.y < 1 ||
          element.x > boardSize ||
          element.y > boardSize) {
        continue;
      }
      if (board != null) {
        //当前位置有子时不绘制
        var chessValue = board.getChessValue(element.x, element.y);
        if (chessValue != 0) {
          continue;
        }
      }

      Color color = colorsAiAnalysis[element.colorIndex];

      Offset offset = Offset(toScreen(element.y), toScreen(element.x));
      canvas.drawOval(Rect.fromCircle(center: offset, radius: tileSize / 2),
          _paint..color = color);
      offset =
          Offset(toScreen(element.y), toScreen(element.x) - tileSize * 1 / 5);

      _drawText(
        canvas: canvas,
        text: element.winRate.toString(),
        textColor: Colors.black54,
        center: offset,
        fontSize: tileSize / 3,
      );

      offset =
          Offset(toScreen(element.y), toScreen(element.x) + tileSize * 1 / 4);
      _drawText(
        canvas: canvas,
        text: element.scoreLead.toStringAsFixed(1),
        textColor: Colors.black54,
        center: offset,
        fontSize: tileSize / 3,
      );
    }
  }

  void paintAINext(AnalysisData analysisData, double Function(int num) toScreen,
      Canvas canvas, double tileSize) {
    for (int i = 0; i < (analysisData.length); i++) {
      var element = analysisData[i];

      Color color = colorsAiNext[element.colorIndex];

      Offset offset = Offset(toScreen(element.y), toScreen(element.x));
      canvas.drawOval(Rect.fromCircle(center: offset, radius: tileSize / 2),
          _paint..color = color);
      offset = Offset(toScreen(element.y), toScreen(element.x));
      _drawText(
        canvas: canvas,
        text: element.winRate.toString(),
        textColor: Colors.black54,
        center: offset,
        fontSize: tileSize / 3,
      );
    }
  }
}

void _drawText({
  required Canvas canvas,
  required String text,
  required Color textColor,
  required Offset center,
  required double fontSize,
  Rect? clearBoundRect,
}) {
  if (clearBoundRect != null) _clearBound(canvas, clearBoundRect);

  TextSpan textSpan = TextSpan(
      style: TextStyle(
          color: textColor, fontSize: fontSize, fontWeight: FontWeight.bold),
      text: text);
  TextPainter textPainter =
      TextPainter(text: textSpan, textDirection: TextDirection.ltr);
  textPainter.layout();
  textPainter.paint(
      canvas,
      Offset(center.dx - textPainter.width / 2,
          center.dy - textPainter.height / 2));
}

void _clearBound(Canvas canvas, Rect rect) {
  canvas.drawRect(
      rect,
      Paint()
        ..color = Colors.transparent
        ..blendMode = BlendMode.clear);
}
